package com.sunday.common.sentinel.study.e4_Types_Rules.study;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.sql.Time;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@RestController
@RequestMapping("/flow")
public class FlowRuleApi {

    private static volatile boolean stop = false;

    /**
     * 流量控制规则 (FlowRule)
     * 流量规则的定义
     * 重要属性：
     * <p>
     * Field	说明	默认值
     * resource	资源名，资源名是限流规则的作用对象
     * count	限流阈值
     * grade	限流阈值类型，QPS 或线程数模式	QPS 模式
     * limitApp	流控针对的调用来源	default，代表不区分调用来源
     * strategy	调用关系限流策略：直接、链路、关联	根据资源本身（直接）
     * controlBehavior	流控效果（直接拒绝 / 排队等待 / 慢启动模式），不支持按调用关系限流	直接拒绝
     * 同一个资源可以同时有多个限流规则。
     */
    protected void initFlowQpsRule() {
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule();
        // 资源名，资源名是限流规则的作用对象
        rule.setResource("HelloWorld");
        // 限流阈值类型，QPS 或线程数模式	QPS 模式
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 限流阈值
        rule.setCount(1);
        // 流控针对的调用来源	default，代表不区分调用来源
        rule.setLimitApp("default");
        // 调用关系限流策略：直接、链路、关联	根据资源本身（直接）
        rule.setStrategy(RuleConstant.STRATEGY_DIRECT);
        // controlBehavior	流控效果（直接拒绝 / 排队等待 / 慢启动模式），不支持按调用关系限流	直接拒绝
        rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }

    protected void initFlowThreadRule() {
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule();
        // 资源名，资源名是限流规则的作用对象
        rule.setResource("HelloWorld");
        // 限流阈值类型，QPS 或线程数模式	QPS 模式
        rule.setGrade(RuleConstant.FLOW_GRADE_THREAD);
        // 限流阈值
        rule.setCount(2);
        // 流控针对的调用来源	default，代表不区分调用来源
        rule.setLimitApp("default");
        // 调用关系限流策略：直接、链路、关联	根据资源本身（直接）
        rule.setStrategy(RuleConstant.STRATEGY_DIRECT);
        // controlBehavior	流控效果（直接拒绝 / 排队等待 / 慢启动模式），不支持按调用关系限流	直接拒绝
        rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }

    /**
     * https://github.com/alibaba/Sentinel/blob/master/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/flow/FlowQpsDemo.java
     */
    @GetMapping("/runQPS")
    public void runQPS() {
        initFlowQpsRule();
        while (true) {
            // 1.5.0 版本开始可以直接利用 try-with-resources 特性
            try (Entry entry = SphU.entry("HelloWorld")) {
                // 被保护的逻辑
                log.info("hello world");
            } catch (BlockException ex) {
                // 处理被流控的逻辑
                log.info("blocked!");
                break;
            }
        }
    }

    /**
     * https://github.com/alibaba/Sentinel/blob/master/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/flow/FlowThreadDemo.java
     */
    @GetMapping("/runThread")
    public void runThread() throws InterruptedException {
        initFlowThreadRule();
        AtomicInteger activeThread = new AtomicInteger(0);
        new Thread(() -> {
            while (!stop) {
                log.info("{}", activeThread);
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                // 1.5.0 版本开始可以直接利用 try-with-resources 特性
                Entry entry = null;
                try {
                    entry = SphU.entry("HelloWorld");
                    activeThread.incrementAndGet();
                    // 被保护的逻辑
                    log.info("hello world");
                } catch (BlockException ex) {
                    // 处理被流控的逻辑
                    log.info("blocked!");
                } finally {
                    if(entry != null){
                        entry.exit();
                        activeThread.decrementAndGet();
                        log.info("release");
                    }
                }
            }).start();
        }

        TimeUnit.SECONDS.sleep(10);
        stop = true;
    }

}
